package fr.umlv.ig.MetroView;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;

import fr.umlv.ig.model.Line;
import fr.umlv.ig.model.MetroModel;
import fr.umlv.ig.model.MetroModelImpl;
import fr.umlv.ig.model.Station;
import fr.umlv.ig.xml.MetroModelFactory;

@SuppressWarnings("serial")
public class MetroMap2 extends JComponent {

	private static int scale = 1;

	private static int stationSize = 4;
	private static int stationHalfSize = 2;

	private final MetroModel metroModel;
	private final BufferedImage mapImage;
	private int topLeftCornerX;
	private int topLeftCornerY;
	private int widthWithScale;
	private int heightWithScale;
	
	private final MouseWheelListener mouseWheelListener;
	private final MouseAdapter mouseAdapter;

	public MetroMap2(final MetroModel metroModel, final BufferedImage mapImage) {
		this.metroModel = metroModel;
		this.mapImage = mapImage;
		
		mouseWheelListener = new MouseWheelListener() {		
			@Override
			public void mouseWheelMoved(MouseWheelEvent e) {
				zoom(e.getWheelRotation(),e.getX(), e.getY());

			}
		};
		
		mouseAdapter = new MouseAdapter() {
			private int x;
			private int y;
			private final MouseAdapter mouseAdapter = new MouseAdapter() {
				
				@Override
				public void mouseDragged(MouseEvent e) {
					translate(x-e.getX(), y-e.getY());
				}		
			};
			
			@Override
			public void mousePressed(MouseEvent e) {
				x = e.getX();
				y = e.getY();
				addMouseMotionListener(mouseAdapter);
			};

			@Override
			public void mouseReleased(MouseEvent e) {
				removeMouseMotionListener(mouseAdapter);
			}
		};
		
		addMouseWheelListener(mouseWheelListener);
		addMouseListener(mouseAdapter);
		
	}
	
	private void translate(int dragX, int dragY) {
		
		topLeftCornerX += dragX/scale;
		topLeftCornerY += dragY/scale;
		
		preparationBeforDraw(widthWithScale,heightWithScale);
		drawMap();
	}

	private void zoom(int zoom, int mouseX, int mouseY) {
		if(zoom<0) {
			scale++;
		}
		else if(scale == 1) {
			return;
		}
		else if(zoom>0) {
			scale--;
		}

		widthWithScale = mapImage.getWidth()/scale;
		heightWithScale = mapImage.getHeight()/scale;
		
		topLeftCornerX = (mapImage.getWidth() - widthWithScale)>>1;
		topLeftCornerY = (mapImage.getHeight() - heightWithScale)>>1;



		System.out.println(" "+topLeftCornerX+" "+topLeftCornerY);
		
		if(zoom>0) {
			preparationBeforDraw(widthWithScale,heightWithScale);
		}
		
		drawMap();	
	}
	
	
	private void preparationBeforDraw(int widthWithScale,int heightWithScale) {


		if(topLeftCornerX <0) {
			topLeftCornerX=0;

			System.out.println("topLeftCornerX <0");
		}
		else if(topLeftCornerX > mapImage.getWidth()) {
			topLeftCornerX = mapImage.getWidth() - widthWithScale;
			System.out.println("topLeftCornerX > mapImage.getWidth()");
		}
		else if(topLeftCornerX + widthWithScale > mapImage.getWidth()) {
			System.out.println("topLeftCornerX + widthWithScale > mapImage.getWidth()");
			topLeftCornerX -= topLeftCornerX+widthWithScale - mapImage.getWidth();
		}
		
		
		if(topLeftCornerY<0) {
			System.out.println("topLeftCornerX <0");
			topLeftCornerY=0;
		}
		else if(topLeftCornerY > mapImage.getHeight()) {
			topLeftCornerY=  mapImage.getHeight() - heightWithScale;
		}
		else if(topLeftCornerY + heightWithScale > mapImage.getHeight()) {
			topLeftCornerX -= topLeftCornerY+heightWithScale - mapImage.getHeight();
		}		
	}
	
	@Override
	public void paintComponent(Graphics g) {        
		g.drawImage(mapImage, 0, 0, 900, 650, null);
	}

	private void drawStation(Station station, Graphics2D gs, Set<String> stationsDraw) {
		stationsDraw.add(station.getName());
		Map<Station,Station> mapNextStations = station.getMapNextStations();
		for(Entry<Station, Station> entryStation : mapNextStations.entrySet()) {
			Station nextStation = entryStation.getValue();
			if(!stationsDraw.contains(nextStation.getName())) {
				gs.drawLine((station.getX()-topLeftCornerX)*scale,
						(station.getY()-topLeftCornerY)*scale,
						(nextStation.getX()-topLeftCornerX)*scale,
						(nextStation.getY()-topLeftCornerY)*scale);
				drawStation(nextStation, gs, stationsDraw);
				//gs.drawString(nextStation.getName(), nextStation.getX(), nextStation.getY());
			}	
		}	
		int x = (station.getX()-topLeftCornerX)*scale - stationHalfSize*scale;
		int y = (station.getY()-topLeftCornerY)*scale - stationHalfSize*scale;
		gs.drawRect(x , y, stationSize*scale  , stationSize*scale);


		repaint();
	}
	
	
	private void drawMap() {
		Graphics2D gs = mapImage.createGraphics();
		gs.setColor(Color.yellow);
		gs.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		gs.clearRect(0, 0, mapImage.getWidth(), mapImage.getHeight());

		int numberOfLine = metroModel.getLineCount();
		for(int i = 0; i<numberOfLine; i++) {
			drawLine(metroModel.getLine(i), gs);
		}
		gs.dispose();		
	}

	private void drawLine(Line line, Graphics2D gs) {
		int numberOfItinerary = line.getCountItinerary();
		Set<String> stationsDraw = new HashSet<String>();
		for(int i=0; i < numberOfItinerary; i++) {
			drawStation( line.getItinerary(i).getStart(),gs, stationsDraw);
		}
	}







	public static void main(String[] args) {
		JFrame f=new JFrame("MetroMap Test");
		JPanel p=new JPanel();
		p.setBackground(Color.YELLOW);
		p.setPreferredSize(new Dimension(900,650));
		p.setSize(900, 650);


		MetroModelFactory metroFactory = null;
		try {
			metroFactory = MetroModelFactory.newInstance();
		} catch (ParserConfigurationException e) {
			System.err.println(e.getMessage());
			return;
		} catch (SAXException e) {
			System.err.println(e.getMessage());
			return;
		} catch (IOException e) {
			System.err.println(e.getMessage());
			return;
		}

		BufferedImage imageMap = new BufferedImage(906, 623, BufferedImage.TYPE_3BYTE_BGR);
		MetroModelImpl model = metroFactory.getMetroModelImpl();
		MetroMap2 metroMap = new MetroMap2(model, imageMap);
		metroMap.drawMap();
		metroMap.setOpaque(true);
		metroMap.setPreferredSize(new Dimension(906,623));
		metroMap.setVisible(true);
		p.add(metroMap);

		f.getContentPane().add(metroMap);
		f.pack();
		f.setDefaultCloseOperation(args.length == 0 ? JFrame.EXIT_ON_CLOSE : WindowConstants.DISPOSE_ON_CLOSE);
		f.setVisible(true);
	}

}
